package generator;

import java.util.List;

public class Code {

	private StringBuffer buffer;
	private int preCounter;
	private int posCounter;
	private int returnPos;
	private boolean hasReturn;
	private String current_method;
	public boolean firstTime;
	
	public Code(){
		buffer = new StringBuffer();
		preCounter = 0;
		posCounter = 0;
		firstTime = true;
	}
	
	public void insertAttributesAndSignatures(String header){
		buffer.append(header);
	}
	
	public void insertTemplate(String metodo) {
		firstTime = false;
		current_method = metodo;
		initiate(current_method);
		int methodPosition = buffer.indexOf(metodo);
		int methodEnd = buffer.indexOf("{",methodPosition);
		String IF_ELSE_CHECK = "{\n"+
								"\t\t\tif(checkAllPre" + current_method + "()){\n"+
								"\t\t\t\texecuteAllPos" + current_method + "();\n"+
								"\t\t\t}\n"+
								"\t\t";
		buffer.replace(methodEnd, methodEnd+1, IF_ELSE_CHECK);
	}
	
	private void initiate(String current_method) {
		int curMethodPos = buffer.indexOf(current_method);
		int defaultReturn = buffer.indexOf("return", curMethodPos);
		if (defaultReturn!= -1 && (defaultReturn - curMethodPos) < 30){
			int endDefaultPos = buffer.indexOf(";", defaultReturn);
			buffer.replace(defaultReturn, endDefaultPos+1, "");
		}
	}

	public void insertCheckPre(String preExpression){
		preCounter++;
		if (preCounter==1){
			String ALLPRE_FIRSTTEMPLATE = "\n\t\tpublic function checkAllPre" + current_method + "():Boolean {\n"+
											"\t\t\treturn checkPre"+preCounter+"();\n"+
											"\t\t}\n";
			int lastBracket = buffer.lastIndexOf("}");
			buffer.replace(lastBracket-2, lastBracket-2, ALLPRE_FIRSTTEMPLATE);			
		}else{
			int lastCheck = buffer.indexOf("checkPre" + (preCounter-1) + "()");
			String ALLPRE_TEMPLATE = " checkPre" + preCounter + "() && ";
			
			buffer.replace(lastCheck-1, lastCheck, ALLPRE_TEMPLATE);
		}
		
		String PRE_TEMPLATE = "\n\t\tpublic function checkPre"+preCounter+"():Boolean {\n"+
								"\t\t\tif("+preExpression+"){\n"+
								"\t\t\t\treturn true;\n"+
								"\t\t\t}else{\n"+
								"\t\t\t\tthrow new Error(\"Erro no metodo checkPre" + preCounter + "()\");\n"+
								"\t\t\t}\n"+
								"\t\t}\n";
		
		
		int lastBracket = buffer.lastIndexOf("}");
		buffer.replace(lastBracket-2, lastBracket-2, PRE_TEMPLATE);
	}
	
	public void insertExecutePos(String posExpression){
		posCounter++;
		boolean containsAtPre = posExpression.contains("@");
		posExpression = posExpression.replace("@pre", "AtPre");
		String param = extractAtPre(posExpression);
		String preVariable = extractPreVariable(posExpression);
		
		if (posCounter==1){
			if(!containsAtPre){
				String ALLPOS_FIRSTTEMPLATE = "\n\t\tpublic function executeAllPos" + current_method + "():void {\n"+
												"\t\t\texecutePos"+posCounter+"();\n"+
												"\t\t}\n";
				int lastBracket = buffer.lastIndexOf("}");
				buffer.replace(lastBracket-2, lastBracket-2, ALLPOS_FIRSTTEMPLATE);

			} else {
				String ALLPOS_FIRSTTEMPLATE = "\n\t\tpublic function executeAllPos" + current_method + "():void {\n"+
												"\t\t\tvar "+param+":* = "+preVariable+";\n"+
												"\t\t\texecutePos"+posCounter+"("+param+");\n"+
												"\t\t}\n";
				int lastBracket = buffer.lastIndexOf("}");
				buffer.replace(lastBracket-2, lastBracket-2, ALLPOS_FIRSTTEMPLATE);
			}
		}else{
			if(!containsAtPre){
				int lastCheck = buffer.indexOf("executePos" + (posCounter-1) + "(");
				int length = buffer.indexOf(";",lastCheck);
				int lastCheckPos = lastCheck + (length-lastCheck);
				String ALLPOS_TEMPLATE = "\n\t\t\texecutePos" + posCounter + "();";
				buffer.replace(lastCheckPos+1, lastCheckPos+1, ALLPOS_TEMPLATE);
			}else{
				String params = containsAtPre ? generateParams(posExpression) : "";
				int lastCheck = buffer.indexOf("executePos" + (posCounter-1) + "(");
				int length = buffer.indexOf(";",lastCheck);
				int lastCheckPos = lastCheck + (length-lastCheck);
				String ALLPOS_TEMPLATE = "\n\t\t\tvar "+param+":* = "+preVariable+";\n"+
											"\t\t\texecutePos" + posCounter + "("+param+");";
				buffer.replace(lastCheckPos+1, lastCheckPos+1, ALLPOS_TEMPLATE);
			}
		}
		
		String POS_TEMPLATE;
		if(posExpression.contains("return")){
			System.out.println("NAOOO!");
			hasReturn = true;
			returnPos = posCounter;
			String type = containsAtPre?":*":"";
			POS_TEMPLATE = "\n\t\tpublic function executePos"+posCounter+"("+param+type+"):"+getCurrentMethodReturnType()+" {\n"+
									"\t\t\t"+removeThis(posExpression)+";\n"+
									"\t\t}\n";
		} else {
			String type = containsAtPre?":*":"";
			POS_TEMPLATE = "\n\t\tpublic function executePos"+posCounter+"("+param+type+"):void {\n"+
								"\t\t\t"+removeThis(posExpression)+";\n"+
								"\t\t}\n";
		}
		
		int lastBracket = buffer.lastIndexOf("}");
		buffer.replace(lastBracket-2, lastBracket-2, POS_TEMPLATE);
	}
	
	private String removeThis(String posExpression) {
		return posExpression.contains("this") ? posExpression.replace("this.", "") : posExpression;
	}

	private String extractPreVariable(String posExpression) {
		String[] paths = posExpression.split("\\s+");
		for (String path : paths) {
			if(path.contains("AtPre")){
				int atPreIndex = path.indexOf("AtPre");
				return path.substring(0,atPreIndex);
			} 
		}
		return "null";
	}

	private String extractAtPre(String posExpression) {
		String[] paths = posExpression.split("\\s+");
		for (String path : paths) {
			if(path.contains(".")){
				String[] subpaths = path.split("\\.");
				for (String subpath : subpaths) {
					if(subpath.contains("AtPre")){
						return subpath;
					}
				}
			}
		}
		return "";
	}

	private String generateParams(String posExpression) {
		return "";
	}

	public String toString(){
		return buffer.toString();
	}

	public void insertAuxMethods(List<StringBuffer> metodos) {
		int lastBracket = buffer.lastIndexOf("}");
		for (StringBuffer sB : metodos) {
			buffer.replace(lastBracket-2, lastBracket-2, sB.toString());
			lastBracket = buffer.lastIndexOf("}");
		}
	}

	public void insertReturn() {
		if(hasReturn){
			//Trecho abaixo insere o "return" antes da chamada do executeAllPos();
			int allPos = buffer.indexOf("executeAllPos" + current_method + "();");
			buffer.replace(allPos-1, allPos, "\t\treturn ");
			
			//Trecho abaixo muda o tipo de retorno do executeAllPos(){} e insere o "return"
			String returnType = getCurrentMethodReturnType();
			allPos = buffer.indexOf("executeAllPos" + current_method + "():");
			int voidPos = buffer.indexOf("void",allPos);
			buffer.replace(voidPos, voidPos+4, returnType);
			
			//Como nao se sabe em que post ele vai colcar o "result", o trecho abaixo pega o
			//codigo de execute que tem o post e joga por ultimo na ordem de chamada
			int lastExecutePost = buffer.indexOf("}",allPos);
			String param = extractParamFromReturnPos();
			buffer.replace(lastExecutePost-1, lastExecutePost-1, "\t\treturn executePos" + returnPos + "("+param+");\n\t");
			
			int returnExecutePos = buffer.indexOf("executePos" + returnPos + "(",allPos);
			int length = buffer.indexOf(";",returnExecutePos);
			int lastExecuteLength = returnExecutePos + (length-returnExecutePos);
			buffer.replace(returnExecutePos-4, lastExecuteLength, "");
		}else{
			//Trecho abaixo de codigo insere o retorno default
			String returnType = getCurrentMethodReturnType();

			int allPos = buffer.indexOf("executeAllPos" + current_method + "(");
			int allPosEnd = buffer.indexOf(";",allPos);
			
			String RETURN = ";\n"+
							"\t\t\t\treturn ";
			if(returnType.equalsIgnoreCase("boolean")){ RETURN += "false;";}
			else if(returnType.equalsIgnoreCase("number")){ RETURN += "0.0;";}
			else if(returnType.equalsIgnoreCase("string")){ RETURN += "\"\";";}
			else if(returnType.equalsIgnoreCase("int")){ RETURN += "0;";}
			else if(returnType.equalsIgnoreCase("void")){ RETURN = "";}
			else { RETURN += "new "+returnType+"();";}
			buffer.replace(allPosEnd, allPosEnd+1, RETURN);
		}
	}

	private String extractParamFromReturnPos() {
		int returnPostIndex = buffer.indexOf("executePos"+returnPos+"(");
		int openParenthesis = buffer.indexOf("(",returnPostIndex);
		int closeParenthesis = buffer.indexOf(")",returnPostIndex);
		return buffer.substring(openParenthesis+1,closeParenthesis);
	}

	private String getCurrentMethodReturnType() {
		int methodPosition = buffer.indexOf(current_method);
		int methodEnd = buffer.indexOf("{",methodPosition);
		int fecharParametros = buffer.indexOf(")", methodPosition);
		int methodLength = methodPosition+(current_method.length())+3; // "method_name():"
//		return buffer.substring(methodLength,methodEnd);
//		System.out.println(buffer.substring(fecharParametros+2,methodEnd));
		return buffer.substring(fecharParametros+2,methodEnd);
	}

	public void preAndPostfixer() {
		if (preCounter==0){
			String ALLPRE_FIRSTTEMPLATE = "\n\t\tpublic function checkAllPre" + current_method + "():Boolean {\n"+
											"\t\t\treturn true;\n"+
											"\t\t}\n";
			int lastBracket = buffer.lastIndexOf("}");
			buffer.replace(lastBracket-2, lastBracket-2, ALLPRE_FIRSTTEMPLATE);	
		}
		if (posCounter==0){
			String ALLPOS_FIRSTTEMPLATE = "\n\t\tpublic function executeAllPos" + current_method + "():void {\n"+
											"\t\t\t\n"+
											"\t\t}\n";
			int lastBracket = buffer.lastIndexOf("}");
			buffer.replace(lastBracket-2, lastBracket-2, ALLPOS_FIRSTTEMPLATE);
		}
	}	
	
}